package picking

import (
	"context"
	"github.com/google/uuid"
	"github.com/shopspring/decimal"
	"github.com/zeromicro/go-zero/core/logx"
	"insighttracking/apps/purchase/auth"
	"insighttracking/apps/purchase/internal/svc"
	"insighttracking/apps/purchase/internal/types"
	"insighttracking/common/utils"
	"insighttracking/query/dao"
	"insighttracking/query/model"
	"insighttracking/query/service"
	"net/http"
	"os"
	"time"
)

type ApiLogic struct {
	logx.Logger
	ctx     context.Context
	svcCtx  *svc.ServiceContext
	Request *http.Request
}

func NewPickingApiLogic(ctx context.Context, svcCtx *svc.ServiceContext, request *http.Request) *ApiLogic {
	hostname, _ := os.Hostname()
	return &ApiLogic{
		Logger: logx.WithContext(ctx).WithCallerSkip(0).WithFields(
			logx.Field("Log_UUID", uuid.New().String()),
			logx.Field("hostname", hostname)),
		ctx:     ctx,
		svcCtx:  svcCtx,
		Request: request,
	}
}

func (l *ApiLogic) GetTaskApi(req *types.PickingGetTaskReq) (resp *types.PickingGetTaskResp, err error) {
	resp = &types.PickingGetTaskResp{}
	accountInfo, err := auth.GetAccountInfo(l.svcCtx, l.Request)
	if err != nil {
		return nil, err
	}
	produceDate := utils.DateStrToTime(req.ProduceDate)
	task, err := service.PickingService.GetTaskApi(l.ctx, accountInfo.EnterpriseId, produceDate)
	list := make([]*types.PickingGetTaskRespList, 0)
	for _, item := range task {
		var orgSpuID int64
		var orgSpuName string
		var baseRequireCount decimal.Decimal
		if item.OrgSpuID == 0 {
			orgSpuID = item.SpuID
			orgSpuName = item.SpuName
			baseRequireCount = item.Weight
		} else {
			orgSpuID = item.OrgSpuID
			orgSpuName = item.OrgSpuName
			baseRequireCount = item.Weight.Div(decimal.New(100, 0).Sub(item.LossRate)).Mul(decimal.New(100, 0)).Round(2)
		}
		unitName, _ := service.SpuService.GetUnitNameBySpuIdApi(l.ctx, orgSpuID)
		list = append(list, &types.PickingGetTaskRespList{
			ProductionDetailID:  item.ProductionPlanDetailID,
			BaseRequireCount:    baseRequireCount,
			BaseSpuID:           orgSpuID,
			BaseSpuName:         orgSpuName,
			BaseSpuUnitName:     unitName,
			LossRate:            item.LossRate,
			ProcessRequireCount: item.Weight,
			ProcessSpuID:        item.SpuID,
			ProcessSpuName:      item.SpuName,
		})
	}
	resp = &types.PickingGetTaskResp{
		ProduceDate: utils.TimestampToString(produceDate),
		List:        list,
	}
	return
}

func (l *ApiLogic) GetScanInfoApi(req *types.PickingGetScanInfoReq) (resp *types.PickingGetScanInfoResp, err error) {
	resp = &types.PickingGetScanInfoResp{}
	accountInfo, err := auth.GetAccountInfo(l.svcCtx, l.Request)
	if err != nil {
		return
	}
	inventory, err := service.PickingService.GetScanInfoApi(l.ctx, accountInfo.EnterpriseId, req.Code)
	if err != nil {
		return
	}
	resp = &types.PickingGetScanInfoResp{
		BatchNumber:    inventory.BatchNumber,
		ExpirationDate: utils.TimestampToString(inventory.ExpirationDate),
		InventoryCount: inventory.InventoryCount,
		ShelfCode:      inventory.ShelfCode,
		SpuID:          inventory.SpuID,
		SpuName:        inventory.SpuName,
	}
	return
}

func (l *ApiLogic) CreateRecordApi(req *types.PickingCreateReq) (resp string, err error) {
	accountInfo, err := auth.GetAccountInfo(l.svcCtx, l.Request)
	if err != nil {
		return
	}

	spuShelfGridInventory, err := service.SpuShelfGridInventoryService.GetBySpuIDApi(l.ctx, accountInfo.EnterpriseId, req.SpuID, req.InventoryBatchNumber)
	if err != nil {
		return
	}
	shelfID := spuShelfGridInventory[0].ShelfID

	storageTime := time.Now()
	batchNumber := utils.StorageBatchNumber(l.svcCtx.RedisDb, storageTime, req.SpuID, "LLPC")
	tx := dao.Use(l.svcCtx.MysqlDb).Begin()

	// 0.领料任务状态
	if _, err = tx.PickingTaskEntity.WithContext(l.ctx).Where(dao.PickingTaskEntity.ProductionPlanDetailID.Eq(req.ProductionDetailID),
		dao.PickingTaskEntity.SpuID.Eq(req.SpuID)).UpdateSimple(dao.PickingTaskEntity.Status.Value(20)); err != nil {
		_ = tx.Rollback()
		return
	}

	// 1.货位库存
	remainingCount, err := service.SpuShelfGridInventoryService.ReduceInventoryApi(l.ctx, tx, accountInfo.EnterpriseId, shelfID, req.SpuID, req.InventoryBatchNumber, req.PickingCount)
	if err != nil {
		_ = tx.Rollback()
		return
	}

	// 2.商品库存
	shelf, _ := service.ShelfService.GetInfoByIdApi(l.ctx, shelfID)
	if err = service.SpuInventoryService.ReduceInventoryApi(l.ctx, tx, accountInfo.EnterpriseId, shelf.WarehouseID, req.SpuID, req.PickingCount); err != nil {
		_ = tx.Rollback()
		return
	}

	// 3.存库记录
	if err = service.InventRecordService.CreateInventRecordApi(l.ctx, tx, &model.InventoryRecordEntity{
		EnterpriseID: accountInfo.EnterpriseId, // 关联到enterprise表主键id
		SpuID:        req.SpuID,                // 商品主键,关联到spu表主键id
		Types:        1,                        // 类型,0表示入库,1表示出库
		Source:       0,                        // 来源,0表示正常出入口,10表示盘点报损报溢,20表调拨
		WarehouseID:  shelf.WarehouseID,        // 仓库id,关联到warehouse表主键id
		ChangeCount:  req.PickingCount,         // 变动数量
		ShelfID:      shelfID,                  // 货位ID
		BatchNumber:  batchNumber,              // 批次号
	}); err != nil {
		_ = tx.Rollback()
		return
	}

	record := &model.PickingRecordEntity{
		ProductionDetailID:   req.ProductionDetailID,
		PickingBatchNumber:   batchNumber,
		InventoryBatchNumber: req.InventoryBatchNumber,
		SpuID:                req.SpuID,
		PickingCount:         req.PickingCount,
		RemainingCount:       remainingCount,
		ShelfID:              shelfID,
	}
	err = service.PickingService.CreateRecordApi(l.ctx, tx, record)
	if err != nil {
		_ = tx.Rollback()
		return
	}
	_ = tx.Commit()
	return
}

func (l *ApiLogic) GetRecordApi(req *types.IdReq) (resp *types.PickingGetRecordResp, err error) {
	list := make([]*types.PickingGetRecordList, 0)
	resp = &types.PickingGetRecordResp{
		List:    list,
		SpuName: "",
	}

	entities, err := service.PickingService.GetRecordApi(l.ctx, req.Id)
	if err != nil || len(entities) == 0 {
		return
	}
	spuID := entities[0].SpuID
	spu, err := service.SpuService.GetBySpuIdApi(l.ctx, spuID)
	if err != nil {
		return
	}

	for _, item := range entities {
		shelf, err := service.ShelfService.GetInfoByIdApi(l.ctx, item.ShelfID)
		if err != nil {
			return nil, err
		}
		list = append(list, &types.PickingGetRecordList{
			InventoryBatchNumber: item.InventoryBatchNumber,
			PickingCount:         item.PickingCount,
			RemainingCount:       item.RemainingCount,
			ShelfCode:            shelf.Code,
		})
	}
	resp.List = list
	resp.SpuName = spu.Name
	return
}

func (l *ApiLogic) GetDefaultBatchApi(req *types.GetDefaultBatchNumberReq) (resp *types.GetDefaultBatchNumberResp, err error) {
	resp = &types.GetDefaultBatchNumberResp{}
	v, err := service.SpuShelfGridInventoryService.GetDefaultBatchNumberApi(l.ctx, req.SpuID)
	if err != nil || v.ShelfCode == "" {
		return
	}
	resp = &types.GetDefaultBatchNumberResp{
		SpuName:              v.SpuName,
		ShelfCode:            v.ShelfCode,
		InventoryBatchNumber: v.BatchNumber,
		CreatedAt:            utils.TimestampToString(v.CreatedAt),
		Supplier:             v.SupplierName,
		InventoryCount:       v.InventoryCount,
		ExpirationDate:       utils.TimestampToString(v.ExpirationDate),
	}
	return
}

func (l *ApiLogic) GetInventoryApi(req *types.GetDefaultBatchNumberReq) (resp *types.GetInventoryResp, err error) {
	resp = &types.GetInventoryResp{}

	accountInfo, err := auth.GetAccountInfo(l.svcCtx, l.Request)
	if err != nil {
		return
	}

	entities, err := service.SpuShelfGridInventoryService.GetInventoryApi(l.ctx, accountInfo.EnterpriseId, req.SpuID)
	if err != nil {
		return
	}
	unitName, _ := service.SpuService.GetUnitNameBySpuIdApi(l.ctx, req.SpuID)
	list := make([]*types.InventoryList, 0)
	for _, v := range entities {
		list = append(list, &types.InventoryList{
			BatchNumber:    v.BatchNumber,
			CreatedAt:      utils.TimestampToString(v.CreatedAt),
			ExpirationDate: utils.TimestampToString(v.ExpirationDate),
			InventoryCount: v.InventoryCount,
			UnitName:       unitName,
			Location:       v.WarehouseName + "/" + v.WarehouseAreaName + "/" + v.ShelfName,
		})
	}
	resp.List = list
	return
}
